package Extras;

import ClasesBase.Casillero;

public class HashTable {

    private Contenedor[] tablaH;
    private Class clase;
    private int cantidadOcupada = 0;
    private boolean reHash = false;

    public HashTable() {
        this(11);
    }

    public HashTable(int k) {
        if (k <= 0 && !esPrimo(k)) {
            k = 11;
        }
        tablaH = new Contenedor[k];
        for (int i = 0; i < tablaH.length; i++) {
            tablaH[i] = new Contenedor();
        }
    }

    public static final boolean esPrimo(int n) {
        if (n == 2) {
            return true;
        }
        if (n % 2 == 0) {
            return false;
        }
        long raiz = (long) Math.sqrt(n);
        for (long div = 3; div <= raiz; div += 2) {
            if (n % div == 0) {
                return false;
            }
        }
        return true;
    }

    public static final int siguientePrimo(int n) {
        if (n <= 1) {
            return 3;
        }
        if (n % 2 == 0) {
            n++;
        }
        for (; !esPrimo(n); n += 2);
        return n;
    }

    private int h(int k) {
        k = Math.abs(k);
        return k % tablaH.length;
    }

    private int realizarExporacionLineal(int h) {
        int j = 1, r = 0;
        do {
            r = (int) (h + Math.pow(j, 2));
            if ((int) r / tablaH.length != 0) {
                r = r % tablaH.length;
            }
            j++;
        } while (tablaH[r].getEstado() == 1);
        return r;
    }

    public boolean put(Comparable x) {
        if (x != null) {
            if (clase == null) {
                if (x.getClass().getSuperclass() == Object.class) {
                    clase = x.getClass();
                } else {
                    clase = x.getClass().getSuperclass();
                }
            }
            if (x.getClass().getSuperclass() != clase && x.getClass() != clase) {
                return false;
            }
            if (porcentajeOcupado() && !reHash) {
                reHash();
            }
            int k = x.hashCode();
            int h = h(k);
            if (tablaH[h].getEstado() == 0 || tablaH[h].getEstado() == 2) {
                tablaH[h].setC(x);
                tablaH[h].setEstado(1);
                cantidadOcupada++;
            } else {//mandar h,
                int i = realizarExporacionLineal(h);
                tablaH[i].setC(x);
                tablaH[i].setEstado(1);
                cantidadOcupada++;
            }

        }
        return true;
    }

    private boolean porcentajeOcupado() {
        double porcentaje;
        porcentaje = (double) (cantidadOcupada * 100 / tablaH.length);
        if (porcentaje > 50) {
            return true;
        }
        return false;
    }

    private void reHash() {
        Contenedor[] c;
        Contenedor[] aux;
        this.reHash = true;
        this.cantidadOcupada = 0;
        aux = tablaH;
        int n = (int) (1.5 * tablaH.length);
        n = siguientePrimo(n);
        c = new Contenedor[n];
        for (int i = 0; i < c.length; i++) {
            c[i] = new Contenedor();
        }
        tablaH = c;
        for (int i = 0; i < aux.length; i++) {
            if (aux[i].getEstado() == 1) {
                this.put(aux[i].getC());
            }
        }
        this.reHash = false;
    }

    public boolean contains(Comparable x) {
        if (cantidadOcupada == 0) {
            return false;
        }

        if (x.getClass().getSuperclass() != clase && x.getClass() != clase) {
            return false;
        }

        int k = x.hashCode();
        int h = h(k);
        int j = 0, r = h;
        while (tablaH[r].getEstado() != 0) {
            if (tablaH[r].getEstado() == 1 && tablaH[r].getC().compareTo(x) == 0) {
                return true;
            }
            j++;
            r = (int) (h + Math.pow(j, 2));
            if ((int) r / tablaH.length != 0) {
                r = r % tablaH.length;
            }
        }
        return false;
    }

    public Comparable getObject(int claveHash) {
        if (tablaH.length == 0) {
            return null;
        }
        int h = h(claveHash);
        int j = 0, r = h;
        while (tablaH[r].getEstado() != 0) {

            if (tablaH[r].getEstado() != 2) {
                if (tablaH[r].getIdObj() == claveHash) {
                    return tablaH[r].getC();
                }
            }

            j++;
            r = (int) (h + Math.pow(j, 2));
            if ((int) r / tablaH.length != 0) {
                r = r % tablaH.length;
            }
        }
        return null;
    }

    public boolean borrar(int claveHash) {
        int h = h(claveHash);
        int j = 0, r = h;
        while (tablaH[r].getEstado() != 0) {

            if (tablaH[r].getIdObj() == claveHash) {
                tablaH[r].setEstado(2);
                cantidadOcupada--;

                return true;
            }
            j++;
            r = (int) (h + Math.pow(j, 2));
            if ((int) (r / tablaH.length) != 0) {
                r = r % tablaH.length;
            }
        }
        return false;
    }

    public int size() {
        return this.cantidadOcupada;
    }

    public void listar() {

        for (int i = 0; i < tablaH.length; i++) {
            if (tablaH[i].getEstado() == 1) {
                System.out.println("Casilla " + i + "Ocupado: " + tablaH[i].getIdObj());
            } else {
                if (tablaH[i].getEstado() == 0) {
                    System.out.println("Casilla " + i + "Esta libre:Libre ");
                } else {
                    System.out.println("Casilla" + i + "Esta libre: Tumba");
                }

            }
        }
    }
}
